# -*- coding: utf-8 -*-

from functools import partial
from decimal import Decimal

from common.utils import math_helper
from .model import BET_TYPE
import itertools

RATE_CONFIG = {
    BET_TYPE.DIRECT_3: 1000.000,
    BET_TYPE.SUM_3: 1000.000,
    BET_TYPE.GROUP_3: 333.333,
    BET_TYPE.GROUP_6: 166.667,
    BET_TYPE.GROUP_3_SUM: 166.667,
    BET_TYPE.RANDOM: 3.690,
    BET_TYPE.DIRECT_2: 100.000,
    BET_TYPE.SUM_2: 100.000,
    BET_TYPE.GROUP_2: 50.000,
    BET_TYPE.GROUP_2_SUM: 50.000,
    BET_TYPE.DWD: 10.000,
    BET_TYPE.BSOE: 4.000
}

UNIT = 2  # 彩票单价


def valid_number(bet_type, number):
    '''同一位之间用','隔开
       不同位之间用'|'隔开
       与顺序无关的选项，使用','隔开
    >>> valid_number(BET_TYPE.DIRECT_3, "0,1,4,6|2|9,8")
    >>> valid_number(BET_TYPE.SUM_3, "4,27")
    >>> valid_number(BET_TYPE.GROUP_3, "1,2")
    >>> valid_number(BET_TYPE.GROUP_6, "1,2,3,4,5")
    >>> valid_number(BET_TYPE.GROUP_3_SUM, "5,22")
    >>> valid_number(BET_TYPE.RANDOM, "0,1,2,3")
    >>> valid_number(BET_TYPE.DIRECT_2, "2|0,9,1")
    >>> valid_number(BET_TYPE.SUM_2, "0,18")
    >>> valid_number(BET_TYPE.GROUP_2, "1,2")
    >>> valid_number(BET_TYPE.GROUP_2_SUM, "1,17")
    >>> valid_number(BET_TYPE.DWD, "||1,5,8")
    >>> valid_number(BET_TYPE.BSOE, "B|O")
    '''
    positions = number.split('|')

    if bet_type in (BET_TYPE.DIRECT_3, BET_TYPE.DIRECT_2, BET_TYPE.DWD, BET_TYPE.BSOE):
        assert len(positions) > 1, 'no `|` expected'
    elif bet_type in (
            BET_TYPE.SUM_3, BET_TYPE.GROUP_3, BET_TYPE.GROUP_6, BET_TYPE.GROUP_3_SUM, BET_TYPE.RANDOM, BET_TYPE.SUM_2,
            BET_TYPE.GROUP_2, BET_TYPE.GROUP_2_SUM):
        assert len(positions) == 1, 'should not have `|`'
    else:
        assert False, 'bet_type invalid'

    for position in positions:
        if not position:
            continue
        ds = position.split(',')
        for d in ds:
            if bet_type in (
                    BET_TYPE.DIRECT_3, BET_TYPE.GROUP_3, BET_TYPE.GROUP_6, BET_TYPE.RANDOM, BET_TYPE.DIRECT_2,
                    BET_TYPE.GROUP_2,
                    BET_TYPE.DWD):
                assert 0 <= int(d) <= 9, 'number range error'
            elif bet_type == BET_TYPE.SUM_3:
                assert 0 <= int(d) <= 27, 'number range error'
            elif bet_type == BET_TYPE.GROUP_3_SUM:
                assert 1 <= int(d) <= 26, 'number range error'
            elif bet_type == BET_TYPE.SUM_2:
                assert 0 <= int(d) <= 18, 'number range error'
            elif bet_type == BET_TYPE.GROUP_2_SUM:
                assert 1 <= int(d) <= 17, 'number range error'
            elif bet_type == BET_TYPE.BSOE:
                assert d in ('B', 'S', 'O', 'E', 'b', 's', 'o', 'e'), 'bet index error'


def calc_sum(number):
    count = 0

    if number == 0:
        count = 1
    if number == 1:
        count = 3
    elif number == 2:
        count = 6
    elif number == 3:
        count = 10
    elif number == 4:
        count = 15
    elif number == 5:
        count = 21
    elif number == 6:
        count = 28
    elif number == 7:
        count = 36
    elif number == 8:
        count = 45
    elif number == 9:
        count = 55
    elif number == 10:
        count = 63
    elif number == 11:
        count = 69
    elif number == 12:
        count = 73
    elif number == 13:
        count = 75
    elif number == 14:
        count = 75
    elif number == 15:
        count = 73
    elif number == 16:
        count = 69
    elif number == 17:
        count = 63
    elif number == 18:
        count = 55
    elif number == 19:
        count = 45
    elif number == 20:
        count = 36
    elif number == 21:
        count = 28
    elif number == 22:
        count = 21
    elif number == 23:
        count = 15
    elif number == 24:
        count = 10
    elif number == 25:
        count = 6
    elif number == 26:
        count = 3
    elif number == 27:
        count = 1

    return count


def calc_group_sum(number):
    count = 0

    if number == 1:
        count = 1
    elif number == 2:
        count = 2
    elif number == 3:
        count = 2
    elif number == 4:
        count = 4
    elif number == 5:
        count = 5
    elif number == 6:
        count = 6
    elif number == 7:
        count = 8
    elif number == 8:
        count = 10
    elif number == 9:
        count = 11
    elif number == 10:
        count = 13
    elif number == 11:
        count = 14
    elif number == 12:
        count = 14
    elif number == 13:
        count = 15
    elif number == 14:
        count = 15
    elif number == 15:
        count = 14
    elif number == 16:
        count = 14
    elif number == 17:
        count = 13
    elif number == 18:
        count = 11
    elif number == 19:
        count = 10
    elif number == 20:
        count = 8
    elif number == 21:
        count = 6
    elif number == 22:
        count = 5
    elif number == 23:
        count = 4
    elif number == 24:
        count = 2
    elif number == 25:
        count = 2
    elif number == 26:
        count = 1

    return count


def calc_sum_two(number):
    count = 0
    n = int(number)
    if n < 10:
        count += (n + 1)
    else:
        count += (19 - n)

    return count


def calc_group_sum_two(number):
    count = 0
    n = int(number)
    if n < 10:
        count += (n + 1) / 2
    else:
        count += (19 - n) / 2

    return count


def calc_total_price(bet_type, number, times=1, unit=UNIT):
    '''根据不同的竞猜方式计算所需总价
    '''
    count = 0

    if bet_type == BET_TYPE.DIRECT_3:
        bet_key = number.split('|')
        result_group = itertools.product(bet_key[0].split(','), bet_key[1].split(','), bet_key[2].split(','))
        for key in result_group:
            count += 1
    elif bet_type == BET_TYPE.SUM_3:
        bet_key = number.split(',')
        for key in bet_key:
            count += calc_sum(int(key))
    elif bet_type == BET_TYPE.GROUP_3:
        bet_key = number.split(',')
        count = math_helper.a(len(bet_key), 2)
    elif bet_type == BET_TYPE.GROUP_6:
        n = len(number.split(','))
        count = math_helper.c(n, 3)
    elif bet_type == BET_TYPE.GROUP_3_SUM:
        bet_key = number.split(',')
        for key in bet_key:
            count += calc_group_sum(int(key))
    elif bet_type == BET_TYPE.RANDOM:
        bet_key = number.split(',')
        for key in bet_key:
            if key in number:
                count += 1
    elif bet_type == BET_TYPE.DIRECT_2:
        bet_key = number.split('|')
        result_group = itertools.product(bet_key[0].split(','), bet_key[1].split(','))
        for key in result_group:
            count += 1
    elif bet_type == BET_TYPE.SUM_2:
        bet_key = number.split(',')
        for key in bet_key:
            count += calc_sum_two(key)
    elif bet_type == BET_TYPE.GROUP_2:
        bet_key = number.split(',')
        result_group = itertools.combinations(bet_key, 2)
        count = len(list(result_group))
    elif bet_type == BET_TYPE.GROUP_2_SUM:
        bet_key = number.split(',')
        for key in bet_key:
            count += calc_group_sum_two(key)
    elif bet_type == BET_TYPE.DWD:
        positions = number.split('|')
        for position in positions:
            if not position:
                continue

            bet_key = position.split(',')
            count += len(bet_key)
    elif bet_type == BET_TYPE.BSOE:
        positions = number.split('|')
        tens = positions[0].split(',')
        ones = positions[1].split(',')
        count = len(tens) * len(ones)

    return count * times * unit


def generate_win_func(number):
    '''根据开奖结果生成根据押注方式对应的中奖函数，避免重复计算
    '''
    result = {}
    number_list = number.split(",")
    number_tuple = tuple(number_list)

    def direct_3(x):  # 使用者下注
        bet_key = x.split('|')
        result_group = itertools.product(bet_key[0].split(','), bet_key[1].split(','), bet_key[2].split(','))

        for result in result_group:
            if number_tuple == result:
                return 1

        return 0

    result[BET_TYPE.DIRECT_3] = direct_3

    def fn(x, y):
        return int(x) + int(y)

    def sum_3(x):  # 使用者下注
        bet_key = x.split(',')
        sum = str(reduce(fn, number_list))

        for key in bet_key:
            if sum == key:
                return 1

        return 0

    result[BET_TYPE.SUM_3] = sum_3

    def group_3(x):  # 使用者下注
        bet_key = x.split(',')
        result_group = itertools.permutations(bet_key, 2)
        formated_result_group = []

        for result in result_group:
            list1 = []
            list1.append(result[0])
            list1.append(result[0])
            list1.append(result[1])
            list2 = []
            list2.append(result[0])
            list2.append(result[1])
            list2.append(result[0])
            list3 = []
            list3.append(result[1])
            list3.append(result[0])
            list3.append(result[0])

            formated_result_group.append(tuple(list1))
            formated_result_group.append(tuple(list2))
            formated_result_group.append(tuple(list3))

        for formated_result in formated_result_group:
            if number_tuple == formated_result:
                return 1

        return 0

    result[BET_TYPE.GROUP_3] = group_3

    def group_6(x):  # 使用者下注
        number_set = set(number_list)
        if len(number_set) == 3:  # 全部号码不相同的情况
            bet_key = x.split(',')
            result_group = itertools.permutations(bet_key, 3)

            for result in result_group:
                if number_tuple == result:
                    return 1

        return 0

    result[BET_TYPE.GROUP_6] = group_6

    def group_3_sum(x):  # 使用者下注
        bet_key = x.split(',')
        number_set = set(number_list)

        if len(number_set) != 1:  # 排除三星全部号码相同的情况
            number_sum = str(reduce(fn, number_list))

            for key in bet_key:
                if number_sum == key:
                    return 1

        return 0

    result[BET_TYPE.GROUP_3_SUM] = group_3_sum

    def random(x):  # 使用者下注
        bet_key = x.split(',')
        amount = 0

        for key in bet_key:
            if key in number_list:
                amount += 1

        return amount

    result[BET_TYPE.RANDOM] = random

    def direct_2(x):  # 使用者下注
        bet_key = x.split('|')
        result_group = itertools.product(bet_key[0].split(','), bet_key[1].split(','))
        number_tuple_2 = tuple(number_list[1:])

        for result in result_group:
            if number_tuple_2 == result:
                return 1

        return 0

    result[BET_TYPE.DIRECT_2] = direct_2

    def sum_2(x):  # 使用者下注
        bet_key = x.split(',')
        sum = str(fn(number_list[1], number_list[2]))

        for key in bet_key:
            if sum == key:
                return 1

        return 0

    result[BET_TYPE.SUM_2] = sum_2

    def group_2(x):  # 使用者下注
        if number_list[1] != number_list[2]:
            bet_key = x.split(',')
            result_group = itertools.permutations(bet_key, 2)
            number_tuple_2 = tuple(number_list[1:])

            for result in result_group:
                if number_tuple_2 == result:
                    return 1

        return 0

    result[BET_TYPE.GROUP_2] = group_2

    def group_2_sum(x):  # 使用者下注
        if number_list[1] != number_list[2]:
            bet_key = x.split(',')
            result = str(fn(number_list[1], number_list[2]))

            for key in bet_key:
                if result == key:
                    return 1

        return 0

    result[BET_TYPE.GROUP_2_SUM] = group_2_sum

    def dwd(x):  # 使用者下注
        bet_key = x.split('|')
        amount = 0

        key0 = bet_key[0].split(',')
        for key in key0:
            if key == number_list[0]:
                amount += 1

        key1 = bet_key[1].split(',')
        for key in key1:
            if key == number_list[1]:
                amount += 1

        key2 = bet_key[2].split(',')
        for key in key2:
            if key == number_list[2]:
                amount += 1

        return amount

    result[BET_TYPE.DWD] = dwd

    def bsoe(x):  # 使用者下注
        bet_key = x.split('|')
        bet_key_tens = bet_key[0].split(',')
        bet_key_ones = bet_key[1].split(',')
        tens = []
        ones = []
        amount = 0

        if int(number_list[1]) > 4:
            tens.append('B')
        else:
            tens.append('S')

        if int(number_list[1]) % 2 == 0:
            tens.append('E')
        else:
            tens.append('O')

        if int(number_list[2]) > 4:
            ones.append('B')
        else:
            ones.append('S')

        if int(number_list[2]) % 2 == 0:
            ones.append('E')
        else:
            ones.append('O')

        for key in bet_key_tens:
            for key2 in bet_key_ones:
                if key.upper() in tens and key2.upper() in ones:
                    amount += 1

        return amount

    result[BET_TYPE.BSOE] = bsoe

    return result


def calc_win_price(funcs, bet_number, bet_type, times=1, unit=UNIT,
                   rate_config=RATE_CONFIG):
    '''根据开奖结果和押注方式/倍数，计算中奖金额
    '''
    func = funcs.get(bet_type)
    base = func(bet_number) * Decimal(RATE_CONFIG.get(bet_type))
    return Decimal(base) * Decimal(times) * Decimal(unit) / Decimal(UNIT)
